package com.gitee.beiding.cloud.easy_call.config;


import com.gitee.beiding.cloud.base.utils.BeanDefinitionUtils;
import com.gitee.beiding.cloud.easy_call.annotation.CallAble;
import com.gitee.beiding.cloud.easy_call.annotation.CanCall;
import com.gitee.beiding.cloud.easy_call.annotation.CanNotCall;
import com.gitee.beiding.cloud.easy_call.annotation.EasyCall;
import com.gitee.beiding.cloud.easy_call.entity.BeanWrapper;
import com.gitee.beiding.cloud.easy_call.entity.TypeMapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;

@Configuration
public class BeanRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware, ApplicationContextAware {


    private ResourceLoader resourceLoader;
    private ClassLoader classLoader;
    private Environment environment;

    private static List<Map<String, Object>> beanWrappers;

    private static Map<String, BeanWrapper> beanWrapperMap;

    private static List<CallCglib<?>> callCglibs = new ArrayList<>();

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        for (CallCglib<?> cglib : callCglibs) {
            cglib.init(applicationContext);
        }

        callCglibs = null;

        for (Map<String, Object> beanWrapper : beanWrappers) {

            String className = (String) beanWrapper.get("className");

            Class<?> aClass;
            try {
                aClass = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }

            String typeMapper = ((String) beanWrapper.get("typeMapper")).trim();
            BeanWrapper bw = new BeanWrapper();
            TypeMapper mapper = applicationContext.getBean(typeMapper, TypeMapper.class);
            bw.setTypeMapper(mapper);

            Object bean = applicationContext.getBean(aClass);
            bw.setBean(bean);

            boolean onlyExportByCanCall = (boolean) beanWrapper.get("onlyExportByCanCall");

            for (Method method : aClass.getMethods()) {

                if (onlyExportByCanCall) {
                    CanCall canCall = method.getAnnotation(CanCall.class);
                    if (canCall != null) {
                        bw.allowMethod(method);
                    }
                } else {
                    CanNotCall canNotCall = method.getAnnotation(CanNotCall.class);
                    if (canNotCall == null) {
                        bw.allowMethod(method);
                    }
                }

            }


            String value = ((String) beanWrapper.get("value")).trim();

            if ("".equals(value)) {
                //使用类型作为名称
                beanWrapperMap.put(aClass.getName(), bw);
            } else {

                beanWrapperMap.put(value, bw);
            }


        }

        beanWrappers = null;
        beanWrapperMap = null;

    }


    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }


    //注册Bean
    private void registerBean(BeanDefinitionRegistry registry, String name, Object bean, boolean primary) {
        BeanDefinition beanDefinition = BeanDefinitionUtils.createBeanDefinition(bean);
        beanDefinition.setPrimary(primary);
        registry.registerBeanDefinition(name, beanDefinition);
    }

    //类扫描器
    private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().getInterfaceNames().length == 1 && Annotation.class.getName().equals(beanDefinition.getMetadata().getInterfaceNames()[0])) {
                        try {
                            Class<?> target = ClassUtils.forName(beanDefinition.getMetadata().getClassName(), BeanRegister.this.classLoader);
                            return !target.isAnnotation();
                        } catch (Exception ex) {
                            this.logger.error("无法加载目标类: " + beanDefinition.getMetadata().getClassName(), ex);
                        }
                    }
                    return true;
                }
                return false;

            }
        };
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        beanWrapperMap = new HashMap<>();
        beanWrappers = new ArrayList<>();
        registerBean(registry, "_easyCall_beanWrapperMap", beanWrapperMap, true);
        registerEasyCallServer(metadata);
        registerEasyCallClient(metadata, registry);
    }

    private void registerEasyCallServer(AnnotationMetadata metadata) {


        //获取类扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();

        //添加注解扫描
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(CallAble.class);
        scanner.addIncludeFilter(annotationTypeFilter);
        scanner.setResourceLoader(this.resourceLoader);

        //扫描所有的Bean
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(ClassUtils.getPackageName(metadata.getClassName()));

        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(CallAble.class.getCanonicalName());

                //TODO 不可能为空
                attributes.put("className", annotationMetadata.getClassName());
                beanWrappers.add(attributes);
            }
        }

    }

    //注册客户端
    private void registerEasyCallClient(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

        //获取类扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();

        //添加注解扫描
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(EasyCall.class);

        scanner.addIncludeFilter(annotationTypeFilter);

        scanner.setResourceLoader(this.resourceLoader);

        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(ClassUtils.getPackageName(metadata.getClassName()));
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {

                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;

                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();

                try {
                    CallCglib<?> cglib = CallCglib.getCglibProxy(Class.forName(annotationMetadata.getClassName()));
                    registerBean(registry, cglib.getName(), cglib.getInstance(), true);
                    callCglibs.add(cglib);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

}
